package org.finesys.common.file.client.ftp;


import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpException;
import cn.hutool.extra.ftp.FtpMode;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import lombok.RequiredArgsConstructor;
import org.apache.commons.net.ftp.FTPFile;
import org.finesys.common.file.FileInfo;
import org.finesys.common.file.client.FileTemplate;
import org.finesys.common.file.properties.FileProperties;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * FTP文件上传
 */
@RequiredArgsConstructor
public class FtpFileTemplate implements FileTemplate {

    private final FileProperties fileProperties;

    private Ftp client;

    @Override
    public void createBucket(String bucketName) {
        String dirPath = getFilePath(bucketName);
        if (!client.existFile(dirPath)) {
            client.mkdir(dirPath);
        }
    }

    @Override
    public FileInfo putObject(String bucketName, String objectName, InputStream stream, String contextType) throws Exception {
        //创建文件夹
        String dirPath = getFilePath(bucketName);
        if (!client.existFile(dirPath)) {
            createBucket(bucketName);
        }
        //上传文件
        client.reconnectIfTimeout();
        boolean success = client.upload(dirPath, objectName, stream);
        if (!success) {
            throw new FtpException(String.format("上传文件到目标目录(%s)失败", dirPath));
        }
        return new FileInfo()
                .setFileId(UUID.randomUUID().toString())
                .setFileName(objectName)
                .setOriginalFileName(objectName)
                .setBasePath(bucketName)
                .setUrl(dirPath + FileUtil.FILE_SEPARATOR + objectName)
                .setSize(stream.available())
                .setFileType(contextType);
    }

    @Override
    public FileInfo putObject(String bucketName, String objectName, InputStream stream) throws Exception {
        return putObject(bucketName, objectName, stream, null);
    }

    @Override
    public void removeBucket(String bucketName) {
        String dirPath = getFilePath(bucketName);
        client.reconnectIfTimeout();
        boolean success = client.delDir(dirPath);
        if (!success) {
            throw new FtpException(String.format("删除目标目录(%s)失败", dirPath));
        }
    }

    @Override
    public void removeObject(String bucketName, String objectName) throws Exception {
        String dirPath = getFilePath(bucketName);
        String filePath = dirPath + FileUtil.FILE_SEPARATOR + objectName;
        client.reconnectIfTimeout();
        boolean success = client.delDir(filePath);
        if (!success) {
            throw new FtpException(String.format("删除目标文件(%s)失败", filePath));
        }
    }

    @Override
    public S3Object getObject(String bucketName, String objectName) {
        client.reconnectIfTimeout();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        client.download(bucketName, objectName, out);
        S3Object s3Object = new S3Object();
        s3Object.setBucketName(bucketName);
        s3Object.setKey(objectName);
        s3Object.setObjectContent(new ByteArrayInputStream(out.toByteArray()));
        return s3Object;
    }

    @Override
    public List<Bucket> getAllBuckets() {
        client.reconnectIfTimeout();
        List<String> files = client.ls(fileProperties.getFtp().getBasePath());
        return files.stream().map(file -> {
            Bucket bucket = new Bucket();
            bucket.setName(file);
            return bucket;
        }).collect(Collectors.toList());
    }

    @Override
    public List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
        client.reconnectIfTimeout();
        String dirPath = fileProperties.getFtp().getBasePath();
        FTPFile[] files = client.lsFiles(dirPath);
        //获取文件信息
        List<S3ObjectSummary> s3ObjectSummaryList = Arrays.stream(files).filter(file -> file.getName().startsWith(prefix)).map(file -> {
            S3ObjectSummary s3ObjectSummary = new S3ObjectSummary();
            s3ObjectSummary.setBucketName(bucketName);
            s3ObjectSummary.setKey(file.getName());
            return s3ObjectSummary;
        }).collect(Collectors.toList());
        //递归查找
        if (recursive) {
            Arrays.stream(files).filter(file -> file.isDirectory()).forEach(file -> getChildFile(file.getLink(), prefix, true, s3ObjectSummaryList));
        }
        return s3ObjectSummaryList;
    }

    /**
     * 获取路径下的文件列表
     */
    private void getChildFile(String rootPath, String prefix, boolean validPrefix, List<S3ObjectSummary> list) {
        FTPFile[] files = client.lsFiles(rootPath);
        Arrays.stream(files).forEach(file -> {
            S3ObjectSummary s3ObjectSummary = new S3ObjectSummary();
            s3ObjectSummary.setBucketName(rootPath);
            s3ObjectSummary.setKey(file.getName());
            //验证前缀
            if (validPrefix) {
                if (file.getName().startsWith(prefix)) {
                    //文件夹继续找
                    if (file.isDirectory()) {
                        getChildFile(file.getLink(), prefix, false, list);
                    } else {
                        list.add(s3ObjectSummary);
                    }
                } else {
                    //文件夹继续找
                    if (file.isDirectory()) {
                        getChildFile(file.getLink(), prefix, true, list);
                    }
                }
            } else {
                //无需验证
                //文件夹继续找
                if (file.isDirectory()) {
                    getChildFile(file.getLink(), prefix, false, list);
                }
                list.add(s3ObjectSummary);
            }
        });
    }


    @Override
    public String shareObjectPreSignedURL(String bucketName, String objectName) {
        return shareObjectPreSignedURL(bucketName, objectName);
    }

    @Override
    public String shareObjectPreSignedURL(String bucketName, String objectName, Integer expires) {
        client.reconnectIfTimeout();
        String filePath = getFilePath(bucketName) + FileUtil.FILE_SEPARATOR + objectName;
        boolean success = client.exist(filePath);
        return success ? filePath : null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        FtpProperties ftpProperties = fileProperties.getFtp();
        this.client = new Ftp(ftpProperties.getHost(), ftpProperties.getPort(), ftpProperties.getUsername(), ftpProperties.getPassword(), StandardCharsets.UTF_8, ftpProperties.getServerLanguageCode(), ftpProperties.getSystemKey(), FtpMode.Passive);
    }

    /**
     * 获取bucketName
     */
    public String getFilePath(String bucketName) {
        String basePath = fileProperties.getFtp().getBasePath();
        if (basePath.endsWith("/") || bucketName.startsWith("/")) {
            return basePath + bucketName;
        }
        return basePath + File.pathSeparator + bucketName;
    }
}
